import { ref } from 'vue'
import { jsPlumb } from 'jsplumb'
import { ElMessage, ElMessageBox } from 'element-plus'
import { deepMerge } from '@sat/shared-utils'
import type {
  ConnectParams,
  Endpoint,
  EndpointOptions,
  jsPlumbInstance,
} from 'jsplumb'
// jsplumb的类型声明文件不准确，这里根据jsplumb源码手写一份声明
interface JsPlumbSetting {
  Container: any // string(selector or id) or element
  Endpoint?: any
  Endpoints?: any[]
  Anchor?: any
  Anchors?: any[]
  PaintStyle?: any
  HoverPaintStyle?: any
  EndpointStyle?: any
  EndpointHoverStyle?: any
  ConnectionsDetachable?: boolean
  ReattachConnections?: boolean
  ConnectionOverlays?: any[]
  DragOptions?: any
  Connector?: any
  DoNotThrowErrors?: boolean
  DropOptions?: any
  EndpointOverlays?: any[]
  EndpointStyles?: any[]
  EndpointHoverStyles?: any[]
  LabelStyle?: any
  ListStyle?: any
  LogEnabled?: boolean
  Overlays?: any[]
  MaxConnections?: number
  RenderMode?: 'svg'
  Scope?: string
}

// 自己内置的配置，源码的逻辑会将默认配置和参数配置进行extends合并
export const defaultSettings: JsPlumbSetting = {
  Container: 'jsPlumbContainer',
  Connector: 'Bezier',
  // 动态锚点、位置自适应
  Anchors: [
    'Top',
    'TopCenter',
    'TopRight',
    'TopLeft',
    'Right',
    'RightMiddle',
    'Bottom',
    'BottomCenter',
    'BottomRight',
    'BottomLeft',
    'Left',
    'LeftMiddle',
  ],
  // 绘制线的样式
  PaintStyle: {
    'stroke': '#84cfff',
    'stroke-width': 2,
  },
  // 绘制线hover的样式
  HoverPaintStyle: {
    'stroke': '#409EFF',
    'stroke-width': 6,
  },
  // 绘制箭头
  Overlays: [
    [
      'Arrow',
      {
        paintStyle: {
          stroke: '#84cfff',
          fill: '#84cfff',
        },
      },
    ],
  ],
  // 端点
  Endpoint: 'Dot',
  EndpointStyle: {
    radius: 8,
    fill: '#84cfff',
  },
}

export type {
  jsPlumbInstance,
  JsPlumbSetting,
  Endpoint,
  EndpointOptions,
  ConnectParams,
}

export function useJsPlumb(settings: JsPlumbSetting, doNotMergeDefaults: boolean = false) {
  // @ts-ignore
  const instance = jsPlumb.getInstance()

  // 是否初始化
  const isReady = ref(false)

  const connectedIds = ref<
    Array<{
      sourceId: string | number
      targetId: string | number
    }>
  >([])

  // TODO 记录所有的端点
  const endpoints = shallowRef<Endpoint[]>([])

  onUnmounted(() => {
    resetJsPlumb()
  })

  /**
   * 重置jsplumb，移除everything
   */
  const resetJsPlumb = () => {
    isReady.value = false
    instance.reset()
    connectedIds.value = []
  }
  /**
   * 初始化jsplumb，functionSlot通常用来自定义处理初始化加载端点、连线等
   */
  const initJsPlumb = (functionSlot?: (instance?: jsPlumbInstance) => any) => {
    resetJsPlumb()
    const lastSettings = doNotMergeDefaults
      ? settings
      : deepMerge<JsPlumbSetting>(defaultSettings, settings)
    instance.importDefaults(lastSettings)
    // 清除端点、连接
    instance.ready(() => {
      // 当连接建立前
      instance.bind('beforeDrop', (conn) => {
        const from = conn.sourceId
        const to = conn.targetId
        if (from === to) {
          ElMessage.error('不能连接自己')
          return false
        }
        return true
      })

      // 双击连接线（删除）
      instance.bind('dblclick', (conn: any, originalEvent: any) => {
        ElMessageBox.confirm('确定删除所点击的线吗?', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning',
        })
          .then(() => {
            instance.deleteConnection(conn)
          })
          .catch(() => {})
      })

      // 连线
      instance.bind('connection', (conn) => {
        connectedIds.value.push({
          sourceId: conn.sourceId,
          targetId: conn.targetId,
        })
      })

      // 改变线的连接节点
      instance.bind('connectionMoved', (conn) => {
        const originalPointIndex = connectedIds.value.findIndex(
          item =>
            item.sourceId === conn.originalSourceId
            && item.targetId === conn.originalTargetId,
        )
        connectedIds.value.splice(originalPointIndex, 1)
      })

      // 删除连线
      instance.bind('connectionDetached', (conn) => {
        const pointIndex = connectedIds.value.findIndex(
          item =>
            item.sourceId === conn.sourceId && item.targetId === conn.targetId,
        )
        connectedIds.value.splice(pointIndex, 1)
      })
      // setSuspendDrawing方法用于暂停jsPlumb的绘制，以便进行批量操作
      instance.setSuspendDrawing(true)
      functionSlot && functionSlot(instance)
      instance.setSuspendDrawing(false, true)
      isReady.value = true
    })
  }

  /**
   * 完成初始化钩子
   */
  const readyPromise = () => {
    return new Promise<boolean>((resolve, reject) => {
      if (isReady.value) {
        resolve(true)
      }
      else {
        const interval = setInterval(() => {
          if (isReady.value) {
            clearInterval(interval)
            resolve(true)
          }
        }, 500)
      }
    })
  }

  /**
   * 修改jsplumb全局配置
   */
  const updateJsPlumbSettings = (settings: JsPlumbSetting) => {
    instance.importDefaults(settings)
  }

  /**
   * 添加端点，强制规范：elNodeId 为 uuid
   */
  const addEndpoint = (
    elNode: string | Element | string[] | Element[],
    params: Partial<EndpointOptions> & {
      uuid: string
      isTarget: boolean
      isSource: boolean
    },
    referenceParams?: Partial<EndpointOptions>,
  ): {
    success: boolean
    message: string
    endpoint?: any
  } => {
    // 添加默认赋值：uuid，maxConnections
    const lastParams = Object.assign(
      {
        maxConnections: 1,
      },
      params,
    )
    const lastReferenceParams: EndpointOptions = Object.assign(
      {
        maxConnections: 1,
      },
      referenceParams ?? {},
    )
    const result: {
      success: boolean
      message: string
      endpoint?: any
    } = {
      success: true,
      message: '添加端点成功',
    }
    const ep = instance.getEndpoint(lastParams.uuid)
    if (!ep) {
      result.endpoint = instance.addEndpoint(
        elNode,
        lastParams,
        lastReferenceParams,
      )
      endpoints.value.push(result.endpoint)
    }
    else {
      result.success = false
      result.message = '当前 ‘params.uuid’ 对应的端点已被添加，请确保其唯一性'
    }
    return result
  }

  /**
   * 移除端点
   */
  const removeEndpoint = (
    uuid: string,
  ): {
    success: boolean
    message: string
  } => {
    const result: {
      success: boolean
      message: string
    } = {
      success: true,
      message: '移除端点成功',
    }
    const endpoint = instance.getEndpoint(uuid)
    if (endpoint) {
      // 这一行可能会内部log错误，TypeError: Cannot read properties of null (reading '_katavorioDrag')，不用管，这是因为在删除端点之前移除了这一端点的挂载元素
      instance.remove(uuid)
      const arr1 = connectedIds.value.filter(
        item => item.targetId != uuid && item.sourceId != uuid,
      )
      connectedIds.value = arr1
      // @ts-ignore
      const index = endpoints.value.findIndex(item => item.elementId == uuid)
      if (index != -1)
        endpoints.value.splice(index, 1)
    }
    else {
      result.success = false
      result.message = '移除端点失败，没有找到当前 ‘uuid’ 对应的端点'
    }
    return result
  }

  /**
   * 连接端点，uuids 和 source/target 二选一
   */
  const connect = (
    params: ConnectParams,
    referenceParams?: Partial<ConnectParams>,
  ) => {
    const connection = instance.connect(params, referenceParams)
    return connection
  }

  /**
   * 批处理程序，最后绘制
   */
  const batchHandler = (fun: Fn) => {
    instance.batch(() => {
      fun()
    })
  }

  return {
    instance,
    connectedIds,
    endpoints,
    isReady,
    readyPromise,
    resetJsPlumb,
    initJsPlumb,
    updateJsPlumbSettings,
    addEndpoint,
    removeEndpoint,
    connect,
    batchHandler,
  }
}

// 附：jsplumb内置的默认配置，从源码copy过来的，仅做参考
const jsPlumbInternalDefault: JsPlumbSetting = {
  Anchor: 'Bottom',
  Anchors: [null, null],
  ConnectionsDetachable: true,
  ConnectionOverlays: [],
  Connector: 'Bezier',
  Container: null,
  DoNotThrowErrors: false,
  DragOptions: {},
  DropOptions: {},
  Endpoint: 'Dot',
  EndpointOverlays: [],
  Endpoints: [null, null],
  EndpointStyle: { fill: '#456' },
  EndpointStyles: [null, null],
  EndpointHoverStyle: null,
  EndpointHoverStyles: [null, null],
  HoverPaintStyle: null,
  LabelStyle: { color: 'black' },
  ListStyle: {},
  LogEnabled: false,
  Overlays: [],
  MaxConnections: 1,
  PaintStyle: { 'stroke-width': 4, 'stroke': '#456' },
  ReattachConnections: false,
  RenderMode: 'svg',
  Scope: 'jsPlumb_DefaultScope',
}
